"ui";
/*
作者QQ:936910749
*/
ui.layout(
    <frame w="*" h="*">
        <canvas id="canvas" />
        <vertical w="*" h="auto" layout_gravity="bottom" bg="#009688">
            <horizontal w="*">
                <card margin="5" cardCornerRadius="3dp" cardElevation="1dp" layout_weight="1">
                    <horizontal margin="5 0">
                        <text text="噪声频率:" textColor="#000000"/>
                        <input id="frequency" text="10" textColor="#000000" inputType="number"/>
                    </horizontal>
                </card>
                <card marginTop="5" marginBottom="5" cardCornerRadius="3dp" cardElevation="1dp" layout_weight="1">
                    <horizontal margin="5 0">
                        <text text="噪声振幅:" textColor="#000000"/>
                        <input id="amplitude" text="50" textColor="#000000" inputType="number"/>
                    </horizontal>
                </card>
                <card marginTop="5" marginBottom="5" marginLeft="5" cardCornerRadius="3dp" cardElevation="1dp" layout_weight="1">
                    <horizontal margin="5 0">
                        <text text="图像宽度:" textColor="#000000"/>
                        <input id="width" text="{{device.width}}" textColor="#000000" inputType="number"/>
                    </horizontal>
                </card>
                <card margin="5" cardCornerRadius="3dp" cardElevation="1dp" layout_weight="1">
                    <horizontal margin="5 0">
                        <text text="图像高度:" textColor="#000000"/>
                        <input id="height" text="{{device.height}}" textColor="#000000" inputType="number"/>
                    </horizontal>
                </card>
            </horizontal>
            <button id="start" text="开始绘制噪声图"/>
            <text text="噪声频率影响绘图时呈现的色环密度" gravity="center"/>
            <text text="噪声振幅影响绘图时呈现的色彩层次" gravity="center"/>
        </vertical>
    </frame>
);

var ASX = new XYToMatrix(ui.canvas, device.width, device.height, 2);
var MapCanvas = new myCanvas(device.width, device.height);
var DrawNoisePicture = new DrawNoisePicture();
var Map = new PerlinNoise();

var paint = new Paint();
ui.canvas.on("draw", function(canvas) {
    if (!MapCanvas) return;
    canvas.drawARGB(255, 0, 0, 0);
    canvas.setMatrix(ASX.matrix);
    canvas.drawBitmap(MapCanvas.bitmap, 0, 0, paint);
});


ui.start.on("click", (view) => {
    Map.frequency = ui.frequency.text();
    Map.amplitude = ui.amplitude.text();
    if(view.text() == "开始绘制噪声图"){
        MapCanvas.clearRect(0, 0, device.width, device.height);
        DrawNoisePicture.Start(ui.width.text(), ui.height.text());
        view.setText("停止绘图");
    }else{
        DrawNoisePicture.Exit();
        view.setText("开始绘制噪声图");
    }
});

function DrawNoisePicture(){
    var drawThread;
    this.Start = function(width, height){
        //根据噪声振幅随机生成不同颜色的画笔
        var paints = [];
        for(let i = 0; i < ui.amplitude.text() * 2; i++){
            paints[i] = new Paint();
            paints[i].setColor(colors.argb(255, random(0, 255), random(0, 255), random(0, 255)));
        }
        drawThread = threads.start(function() {
            for(let i = 1; i < height; i++){
                for(let j = 1; j < width; j++){
                    var h = Math.floor(Map.Perlin(i / 1000, j / 1000));
                    MapCanvas.drawPoint(j, i, paints[h + Math.floor(paints.length / 2)]);
                }
            }
            setInterval(() => {}, 1000);
        });
    }
    this.Exit = function(){
        if(drawThread.isAlive()){
            drawThread.interrupt();
        }
    }
}

//drawThread.setTimeout(() => {
    
//}, 0);

function PerlinNoise() {
    //生成0-255的不重复随机数列(用于通过哈希运算确定晶格各顶点的梯度向量)
    var permutation = [];
    var nums = [];
    for(var i = 0; i < 256; i++){
        nums[i] = i;
    }
    for(var i = 0; i < 256; i++){
        var index = random(0, nums.length - 1);
        permutation[i] = nums[index];
        nums.slice(index, 1);
    }
    
    //将permutation数组再次填充相同的数列，避免在后期的哈希运算中出现数组越界的情况
    var p = [];
    for(let x = 0; x < 512; x++){
        p[x] = permutation[x % 256];
    }
    
    //噪声重复的区间范围
    this.repeat = 255;
    //噪声的频率
    this.frequency = 1;
    //噪声的振幅
    this.amplitude = 1;
    //柏林噪声入口函数，输入值为(float, float)表示二维平面上的一个坐标, 返回一个float噪声值
    this.Perlin = function(x, y){
        x *= this.frequency;
        y *= this.frequency;
        //确定柏林噪声重复的区间
        if(this.repeat > 0){
            x %= this.repeat;
            y %= this.repeat;
        }
        //取xy的整数部分用于确定坐标落在了哪个晶格内
        var xi = Math.floor(x) % 255;
        var yi = Math.floor(y) % 255;
        //取xy的小数部分用于确定坐标在晶格内的位置
        var xf = x - Math.floor(x);
        var yf = y - Math.floor(y);
        //对坐标的小数部分使用缓和曲线插值
        var u = Fade(xf);
        var v = Fade(yf);
        /*通过哈希运算确定晶格4个顶点的哈希值(用于在Grad函数中确定顶点的梯度向量)
        b-----c
        |     |
        |     |
        a-----d     */
        var a, b, c, d;
        a = p[p[xi] + yi];
        b = p[p[xi] + Inc(yi)];
        c = p[p[Inc(xi)] + Inc(yi)];
        d = p[p[Inc(xi)] + yi];
        //计算4个顶点的梯度向量与输入点-顶点向量之间的点积的加权平均值
        var x1, x2, z;
        x1 = Lerp(Grad(a, xf, yf), Grad(d, xf - 1, yf), u);
        x2 = Lerp(Grad(b, xf, yf - 1), Grad(c, xf - 1, yf - 1), u);
        z = Lerp(x1, x2, v);
        return z * this.amplitude;
    };
    function Lerp (a, b, x){
        return a + x * (b - a);
    }
    function Grad(hash, x, y) {
        var v = [{x: 0, y: 1}, {x: 1, y: 0}, {x: 1, y: 1}, {x: 0, y: -1}, {x: -1, y: 0}, {x: -1, y: -1}, {x: 1, y: -1}, {x: -1, y: 1}];
        return Dot(Normalized(v[hash % 8]), {x: x, y: y});
    }
    //缓和曲线插值
    function Fade(t){
        // 6t^5 - 15t^4 + 10t^3
        return t * t * t * (t * (t * 6 - 15) + 10);
    }
    function Inc (num){
        num++;
        if (this.repeat > 0) num %= this.repeat;
        return num;
    }
    //向量模长
    function Magnitude(v) {
        return Math.sqrt(v.x * v.x + v.y * v.y);
    }
    //向量归一化
    function Normalized(v) {
        var m = Magnitude(v);
        return {x: v.x / m, y: v.y / m};
    }
    //两个向量点乘
    function Dot(v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    }
}

//这个工具类来自3000示例生僻字的触摸模块
function XYToMatrix (canvas, SCREEN_WIDTH, SCREEN_HEIGHT, maxPoints) {
    var matrix = new android.graphics.Matrix();
    var gameRect = new android.graphics.RectF(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
    var canvasRect = new android.graphics.RectF(0, 0, device.width, device.width * (SCREEN_HEIGHT / SCREEN_WIDTH));
    matrix.setRectToRect(gameRect, canvasRect, android.graphics.Matrix.ScaleToFit.CENTER);

    this.matrix = matrix || new android.graphics.Matrix;
    this.invertMatrix = new android.graphics.Matrix;
    this.matrix.invert(this.invertMatrix);
    this.getScaling = function(ary) {
        ary = Array.isArray(ary) ? ary : [0, 0, 100, 100];
        try {
            var Ary = this.matrixPoints(this.matrix, ary);
            return this.weiyi([Ary[2] - Ary[0], Ary[3] - Ary[1]]) / this.weiyi(ary);
        } catch (e) {
            toastLog(e);
        };
    };
    this.maxPoints = maxPoints || 2;
    this.maxPointsListener = () => {};
    this.Touch = {
        Matrix: this.matrix,
        PointStart: new Array,
        PointCurrent: new Array,
    };
    var sX, sY;
    canvas.setOnTouchListener(new android.view.View.OnTouchListener((view, event) => {
        try {
            var W = view.getWidth();
            var H = view.getHeight();
            var PC = event.getPointerCount();
            switch (event.getActionMasked()) {
                case event.ACTION_MOVE:
                    //滑动
                    try {
                        for (let i = 0; i < PC; i++) {
                            let id = event.getPointerId(i);
                            let x = event.getX(i);
                            let y = event.getY(i);
                            this.Touch.PointCurrent[i * 2] = x;
                            this.Touch.PointCurrent[i * 2 + 1] = y;
                        };
                        //记录当前各手指坐标信息。
                        if (PC > this.maxPoints) { //手指数大于4个虽然记录坐标信息，但是不进行矩阵操作。
                            this.maxPointsListener(view, event);
                            break;
                        };
                        var Matrix = new android.graphics.Matrix();
                        Matrix.setPolyToPoly(this.Touch.PointStart, 0, this.Touch.PointCurrent, 0, PC > 4 ? 4 : PC);
                        this.matrix = new android.graphics.Matrix();
                        this.matrix.setConcat(Matrix, this.Touch.Matrix);
                        //进行矩阵运算并刷新矩阵。
                        this.matrix.invert(this.invertMatrix);
                        //反矩阵
                    } catch (e) {
                        throw "MOVE " + e;
                    };
                    break;
                case event.ACTION_CANCEL:
                    //log("CANCEL");
                    this.Touch.PointStart = new Array;
                    this.Touch.PointCurrent = new Array;

                    break;
                case event.ACTION_OUTSIDE:
                    //log("OUTSIDE");

                    break;
                default:
                    var I = Math.floor(event.getAction() / 256);
                    var ID = event.getPointerId(I);
                    var X = event.getX(I);
                    var Y = event.getY(I);
                    switch (event.getActionMasked()) {
                        case event.ACTION_DOWN:
                            sX = event.getRawX();
                            sY = event.getRawY();
                            try {
                                log("down");
                                //当有新的手指按下时使坐标差为零。//开始新的多指矩阵运算方式
                                this.Touch.PointStart.splice(I * 2, 0, X, Y);
                                this.Touch.PointCurrent.splice(I * 2, 0, X, Y);
                                this.Touch.Matrix = this.matrix;
                                //log(this.Touch.Matrix);
                            } catch (e) {
                                throw "DOWN " + e;
                            };
                            break;
                        case event.ACTION_UP:
                            //最后一个手指抬起。
                            log("up");
                            this.Touch.PointStart = new Array;
                            this.Touch.PointCurrent = new Array;
                            if (Math.abs(event.getRawY() - sY) < 5 && Math.abs(event.getRawX() - sX) < 5) {
                                
                            }
                            break;
                        case event.ACTION_POINTER_DOWN:
                            log("POINTER_DOWN");
                            try {
                                //当有新的手指按下时使坐标差为零。//开始新的多指矩阵运算方式
                                this.Touch.PointStart.splice(I * 2, 0, X, Y);
                                this.Touch.PointCurrent.splice(I * 2, 0, X, Y);
                                //获取点的总数量。
                                this.Touch.Matrix = this.matrix;
                                for (let i = 0; i < PC; i++) {
                                    this.Touch.PointStart[i * 2] = this.Touch.PointCurrent[i * 2];
                                    this.Touch.PointStart[i * 2 + 1] = this.Touch.PointCurrent[i * 2 + 1];
                                };
                                //保存坐标的数组。
                                if (PC > this.maxPoints) { //手指数大于4个化为原始矩阵虽然记录坐标信息，但是不进行矩阵操作。
                                    this.maxPointsListener(view, event);
                                    break;
                                };

                                var Matrix = new android.graphics.Matrix();
                                Matrix.setPolyToPoly(this.Touch.PointStart, 0, this.Touch.PointCurrent, 0, PC > 4 ? 4 : PC);
                                this.matrix = new android.graphics.Matrix();
                                this.matrix.setConcat(Matrix, this.Touch.Matrix);
                                //进行矩阵运算并刷新矩阵。
                                this.matrix.invert(this.invertMatrix);
                                //反矩阵
                            } catch (e) {
                                throw "P_DOWN " + e;
                            };
                            break;
                        case event.ACTION_POINTER_UP:
                            log("POINTER_UP");
                            try {
                                this.Touch.Matrix = this.matrix;
                                for (let i = 0; i < PC; i++) {
                                    this.Touch.PointStart[i * 2] = this.Touch.PointCurrent[i * 2];
                                    this.Touch.PointStart[i * 2 + 1] = this.Touch.PointCurrent[i * 2 + 1];
                                };
                                this.Touch.PointStart.splice(I * 2, 2);
                                this.Touch.PointCurrent.splice(I * 2, 2);

                            } catch (e) {
                                throw "P_UP " + e;
                            };
                            break;
                    };
            };
        } catch (e) {
            log("imgTouch: " + e);
        };
        return true;
    }));
    this.matrixPoints = function(matrix, ary) {
        //通过矩阵运算坐标数组。但是需要转换为浮点数组。
        var ary = this.toJavaArray("float", ary);
        matrix.mapPoints(ary);
        return this.toJsArray(ary);
    };
    this.toJavaArray = function(type, ary) {
        //var Ary = java.lang.reflect.Array.newInstance(		java.lang.Float.TYPE, 4);
        var Ary = util.java.array(type, ary.length);
        for (let i in ary) {
            Ary[i] = ary[i];
        };
        return Ary;
    };
    this.toJsArray = function(ary) {
        var Ary = new Array(ary.length);
        for (let i in ary) {
            Ary[i] = ary[i];
        };
        return Ary;
    };
    this.getsd = (s, ary) => {
        var sum = this.weiyi(ary);
        var S = (s / sum) || 0;
        for (var i = 0; i < ary.length; i++) {
            ary[i] = ary[i] * S;
        };
        return ary;
    };
    this.weiyi = function(ary) {
        var sum = 0;
        for (var i = 0; i < ary.length; i++) {
            sum += Math.pow(ary[i], 2);
        };
        return Math.sqrt(sum);
    };
    this.kdfx = function(Y) {
        var x = Math.cos(Y % 360 / 360 * 2 * Math.PI);
        var y = Math.sin(Y % 360 / 360 * 2 * Math.PI);
        return [x, y];
    };
    this.ydfx = (ary) => {
        var ary = this.getsd(1, ary);
        var x = ary[0],
            y = ary[1];
        var Y = Math.asin(y) / (2 * Math.PI) * 360;
        if (x < 0) {
            Y = 180 - Y;
        };
        return Y;
    };
}

//这个工具类来自商店坦克大战游戏
function myCanvas(width, height) {
    this.width = width;
    this.height = height;
    this.bitmap = android.graphics.Bitmap.createBitmap(this.width || 1, this.height || 1, android.graphics.Bitmap.Config.ARGB_8888);
    this.canvas = new Canvas(this.bitmap);
    this.save = function() {}; //this.canvas.save;
    this.restore = function() {}; //this.canvas.restore;
    this.paint = new android.graphics.Paint;
    this.paint.setStrokeWidth(5);
    this.setColor = function(color) {
        this.paint.setColor(colors.parseColor(color));
    };
    this.drawImage = function(imageobj, Ax, Ay, Aw, Ah, Bx, By, Bw, Bh) {
        this.paint.setXfermode(new android.graphics.PorterDuffXfermode(android.graphics.PorterDuff.Mode.SRC));
        //imageobj.read();
        if (arguments.length == 9) {
            this.canvas.drawImage(imageobj, Ax, Ay, Aw, Ah, Bx, By, Bw, Bh, this.paint);
        } else {
            this.canvas.drawImage(imageobj, Ax, Ay, this.paint);
        }
    };
    this.clearRect = function(Ax, Ay, Aw, Ah) {
        this.paint.setXfermode(new android.graphics.PorterDuffXfermode(android.graphics.PorterDuff.Mode.CLEAR));
        this.canvas.drawRect(Ax, Ay, Ax + Aw, Ay + Ah, this.paint);
    };
    this.fillRect = function(Ax, Ay, Aw, Ah) {
        this.paint.setStyle(Paint.Style.FILL);
        this.paint.setXfermode(new android.graphics.PorterDuffXfermode(android.graphics.PorterDuff.Mode.SRC));
        this.canvas.drawRect(Ax, Ay, Ax + Aw, Ay + Ah, this.paint);
    };
    this.drawLine = function(fx, fy, ex, ey, paint){
        this.canvas.drawLine(fx, fy, ex, ey, paint);
    };
    this.drawPoint = function(x, y, paint){
        this.canvas.drawPoint(x, y, paint);
    };
}
